﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Mirror.CliMetadata.Tables;
using Kokomo.Mirror.CliMetadata.Signatures;
using System.Runtime.InteropServices;
using System.IO;
using Kokomo.Mirror.PeFormat;

namespace Kokomo.Mirror.CliMetadata
{
	class CliFieldInfo : FieldInfo, IExtendedFieldInfo
	{
		public CliFieldInfo(
			FieldRow fieldRow,
			CliMemberBasicInfo basicInfo
			)
		{
			this._fieldRow = fieldRow;
			this.basicInfo = basicInfo;
			//this._signature = (FieldSignature)fieldRow.Signature.Signature;
			//this._customAttributeData = new Lazy<IList<CustomAttributeData>>(this.LoadCustomAttributeData);
			//this._constantValue = new Lazy<object>(this.LoadConstantValue);
		}

		private CliMemberBasicInfo basicInfo;
		private FieldRow _fieldRow;
		//private FieldSignature _signature;

		private FieldSignature LoadSignature()
		{
			return (FieldSignature)this.basicInfo.module.GetSignature(this._fieldRow.Signature);
		}

		public override int MetadataToken
		{
			get { return this.basicInfo.token; }
		}
		public override Module Module
		{
			get { return this.basicInfo.module; }
		}

		public override FieldAttributes Attributes
		{
			get { return this._fieldRow.Flags; }
		}

		public override RuntimeFieldHandle FieldHandle
		{
			get { throw new NotImplementedException(); }
		}

		public override Type FieldType
		{
			get
			{
				Type[] genericTypeArguments = null;
				if (
					((object)this.DeclaringType != null)
					)
					genericTypeArguments = this.DeclaringType.GetGenericArguments();
				return this.LoadSignature().type.ResolveType(this.basicInfo.module, true, genericTypeArguments, null);
			}
		}

		#region Attributes

		//private Lazy<IList<CustomAttributeData>> _customAttributeData;
		private IList<CustomAttributeData> LoadCustomAttributeData()
		{
			return this.basicInfo.module.GetCustomAttributesFor(this.MetadataToken);
		}
		public override IList<CustomAttributeData> GetCustomAttributesData()
		{
			//return this._customAttributeData.Value;
			return this.LoadCustomAttributeData();
		}
		#endregion

		//private Lazy<object> _constantValue;
		private object LoadConstantValue()
		{
			return this.basicInfo.module.QueryConstantValue(this.MetadataToken);
		}

		public override object GetRawConstantValue()
		{
			if (this.IsLiteral)
				return this.LoadConstantValue(); //return this._constantValue.Value;

			throw new NotImplementedException();
		}

		public override object GetValue(object obj)
		{
			throw new NotImplementedException();
		}

		public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}

		public override Type DeclaringType
		{
			get { return this.basicInfo.declaringType; }
		}

		public override object[] GetCustomAttributes(Type attributeType, bool inherit)
		{
			throw new NotImplementedException();
		}

		public override object[] GetCustomAttributes(bool inherit)
		{
			throw new NotImplementedException();
		}

		public override bool IsDefined(Type attributeType, bool inherit)
		{
			throw new NotImplementedException();
		}

		public override string Name
		{
			get { return this.basicInfo.module.Metadata.GetString(this._fieldRow.Name); }
		}

		public override Type ReflectedType
		{
			get { return this.basicInfo.reflectedType; }
		}

		public uint Rva
		{
			get { return (uint)this.basicInfo.module.QueryFieldRva(this.MetadataToken); }
		}

		public byte[] GetRawData()
		{
			Type fieldType = this.FieldType;

			byte[] data = null;
			StructLayoutAttribute layoutAttribute = fieldType.StructLayoutAttribute;
			if ((layoutAttribute != null) && (layoutAttribute.Size > 0))
			{
				data = new byte[layoutAttribute.Size];
				PeModule pe = this.basicInfo.module.peModule;
				using (Stream stream = pe.GetStream())
				{
					stream.Position = pe.RvaToFileOffset(this.Rva).Value;
					stream.Read(data, 0, layoutAttribute.Size);
				}
			}

			return data;
		}

		public FieldInfo Reflect()
		{
			Type declaring = this.DeclaringType.Reflect();
			FieldInfo reflected = declaring.GetField(this.Name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			return reflected;
		}
	}
}
